/**
 * @author mrdoob / http://mrdoob.com/
 */

import * as THREE from '../../js/threejs/build/three.module.js'

import {TransformControls} from '../../js/threejs/jsm/controls/TransformControls.js'

import {UIPanel} from '../../js/threejs/libs/ui.js'

import {EditorControls} from '../../js/threejs/EditorControls.js'
import {ViewHelper} from './Viewport.ViewHelper.js'

import Toolbar from "./Toolbar.js";
import DeviceToolMenu from "./Device.Tool.menu.js";

import {Line2} from '../../js/threejs/jsm/lines/Line2.js';
import {LineMaterial} from '../../js/threejs/jsm/lines/LineMaterial.js';
import {LineGeometry} from '../../js/threejs/jsm/lines/LineGeometry.js';
import {ViewSunlight} from "./Viewport.Sunlight.js";
import DeviceObject from "./Device.Object.js";

var Viewport = function (displayer) {
  var signals = displayer.signals

  var container = new UIPanel()
  container.setId('viewport')
  container.setPosition('absolute')
  container.setWidth(displayer.VIEWPORT_WIDTH)
  container.setHeight(displayer.VIEWPORT_HEIGHT)

  // 左上角工具栏
  container.add(new Toolbar(displayer).container)
  // 跟随设备的右键菜单
  container.add(new DeviceToolMenu(displayer).container)

  this.container = container

  //

  var renderer = null
  var pmremGenerator = null

  var camera = displayer.camera
  var scene = displayer.scene
  var sceneHelpers = displayer.sceneHelpers
  var showSceneHelpers = true
  var enableTransformControls = true

  var dbclickTimer

  var objects = []

  // helpers
  var viewHelper = new ViewHelper(camera, container)

  var sunlight = new ViewSunlight(displayer, camera)

  // 对象被选中时的框
  var box = new THREE.Box3()

  var selectionBox = new THREE.BoxHelper()
  selectionBox.material.depthTest = false
  selectionBox.material.transparent = false
  selectionBox.visible = false
  sceneHelpers.add(selectionBox)

  var transformControls = window.transformControls = new TransformControls(camera, container.dom)

  transformControls.addEventListener('change', function () {
    var object = transformControls.object

    if (object !== undefined) {
      selectionBox.setFromObject(object)

      var helper = editor.helpers[object.id]

      if (helper !== undefined && helper.isSkeletonHelper !== true) {
        helper.update()
      }

      signals.refreshSidebarObject3D.dispatch(object)
    }

    render()
  })
  sceneHelpers.add(transformControls)

  // 对象选择

  var raycaster = new THREE.Raycaster()
  var mouse = new THREE.Vector2()

  // 设备拓扑结构划线group
  var topologyGroup = new THREE.Group();
  scene.add(topologyGroup);

  // events

  function getIntersects(point, objects) {
    mouse.set(point.x * 2 - 1, -(point.y * 2) + 1)

    raycaster.setFromCamera(mouse, camera)

    return raycaster.intersectObjects(objects)
  }

  var onDownPosition = new THREE.Vector2()
  var onUpPosition = new THREE.Vector2()
  var onDoubleClickPosition = new THREE.Vector2()

  function getMousePosition(dom, x, y) {
    var rect = dom.getBoundingClientRect()
    return [(x - rect.left) / rect.width, (y - rect.top) / rect.height]
  }

  function handleClick(e) {
    if (onDownPosition.distanceTo(onUpPosition) === 0) {
      let intersects = getIntersects(onUpPosition, objects)
      let object = null
      if (intersects.length > 0) {
        object = displayer.getWholeObject(intersects[0].object)
        if (object.userData.type === 'sceneGroup') {
          // zhouwr, 数据展示页，点击对象是整个现场场景时，不选中
          object = null
        }
      }
      if (e.button === 0) {
        signals.onclick.dispatch(object)
      } else if (e.button === 2) {
        signals.onrightclick.dispatch(object)
      }
    }
    render()
  }

  function onMouseDown(event) {
    // event.preventDefault();

    let array = getMousePosition(container.dom, event.clientX, event.clientY)
    onDownPosition.fromArray(array)

    document.addEventListener('mouseup', onMouseUp, false)
  }

  function onMouseUp(event) {
    let array = getMousePosition(container.dom, event.clientX, event.clientY)
    onUpPosition.fromArray(array)

    clearTimeout(dbclickTimer)
    dbclickTimer = setTimeout(() => {
      handleClick(event)
    }, 400)

    document.removeEventListener('mouseup', onMouseUp, false)
  }

  function onTouchStart(event) {
    let touch = event.changedTouches[0]

    let array = getMousePosition(container.dom, touch.clientX, touch.clientY)
    onDownPosition.fromArray(array)

    document.addEventListener('touchend', onTouchEnd, false)
  }

  function onTouchEnd(event) {
    let touch = event.changedTouches[0]

    let array = getMousePosition(container.dom, touch.clientX, touch.clientY)
    onUpPosition.fromArray(array)

    handleClick()

    document.removeEventListener('touchend', onTouchEnd, false)
  }

  function onDoubleClick(event) {
    clearTimeout(dbclickTimer)
    let array = getMousePosition(container.dom, event.clientX, event.clientY)
    onDoubleClickPosition.fromArray(array)

    let intersects = getIntersects(onDoubleClickPosition, objects)

    if (intersects.length > 0) {
      let object = displayer.getWholeObject(intersects[0].object)
      if (object.userData.type === 'sceneGroup') {
        // zhouwr, 数据展示页，点击对象是整个现场场景时，不选中
        object = null
      }
      signals.ondbclick.dispatch(object)
    }
  }

  signals.onclick.add((object) => {
    console.log('鼠标左键')
    displayer.select(object)
    if (object) {
      object.userData.rightMenu = false
    }
    signals.deviceMenued.dispatch(null)
  })

  signals.onrightclick.add((object) => {
    console.log('鼠标右键')
    displayer.select(object)
    if (object) {
      object.userData.rightMenu = true
    }
    signals.deviceMenued.dispatch(object)
  })

  signals.ondbclick.add((object) => {
    console.log('鼠标双击')
    displayer.select(object)
    if (object) {
      object.userData.rightMenu = false
    }
    signals.deviceMenued.dispatch(null)
  })

  container.dom.addEventListener('mousedown', onMouseDown, false)
  container.dom.addEventListener('touchstart', onTouchStart, false)
  container.dom.addEventListener('dblclick', onDoubleClick, false)

  var controls = new EditorControls(camera, container.dom)
  controls.addEventListener('change', function () {
    signals.cameraChanged.dispatch(camera)
  })

  viewHelper.controls = controls

  signals.rendererUpdated.add(function () {
    render()
  })

  signals.rendererChanged.add(function (newRenderer, newPmremGenerator) {
    if (renderer !== null) {
      container.dom.removeChild(renderer.domElement)
    }

    renderer = newRenderer
    pmremGenerator = newPmremGenerator

    renderer.autoClear = false
    renderer.autoUpdateScene = false
    renderer.outputEncoding = THREE.sRGBEncoding
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setSize(container.dom.offsetWidth, container.dom.offsetHeight)

    container.dom.appendChild(renderer.domElement)

    render()
  })

  signals.sceneGraphChanged.add(function () {
    render()
  })

  signals.cameraChanged.add(function () {
    render()
  })

  signals.objectSelected.add(function (object) {
    selectionBox.visible = false
    if (object !== null && object !== scene && object !== camera) {
      displayer.selected = object
      box.setFromObject(object)
      if (box.isEmpty() === false) {
        selectionBox.setFromObject(object)
        let color = new THREE.Color()
        color.setHSL(Math.abs(object.position.x + object.position.y + object.position.z), 1, 0.5);
        selectionBox.material.color = color
        selectionBox.visible = true
      }
    }
    // render()
  })

  signals.objectFocused.add(function (object) {
    signals.showHelpersChanged.dispatch(true)
    // 定位
    controls.focus(object)
  })

  signals.geometryChanged.add(function (object) {
    if (object !== undefined) {
      selectionBox.setFromObject(object)
    }

    render()
  })
  /**
   * 设备拓扑实现划线
   */
  signals.deviceLine.add(function (object1, object2) {
    // 对象拓扑结构划线
    let pointArr = []
    // Position and THREE.Color Data
    const color = new THREE.Color();
    let colors = [];
    // 设备拓扑划线
    let geometry = new LineGeometry();
    let material = new LineMaterial({
      linewidth: 3,
      // color: 0xffffff,
      vertexColors: true
    });
    material.resolution.set(displayer.VIEWPORT_WIDTH, displayer.VIEWPORT_HEIGHT); //这句如果不加宽度仍然无效
    pointArr.push(object1.position.x, object1.position.y, object1.position.z)
    pointArr.push(object2.position.x, object2.position.y, object2.position.z)
    color.setHSL(30 + (Math.abs(object1.position.x + object1.position.y + object1.position.z) % 250), 1, 0.5);
    colors.push(color.r, color.g, color.b)
    color.setHSL(30 + (Math.abs(object2.position.x + object2.position.y + object2.position.z) % 250), 1, 0.5);
    colors.push(color.r, color.g, color.b)
    console.log(Math.abs(object1.position.x + object1.position.y + object1.position.z), Math.abs(object2.position.x + object2.position.y + object2.position.z))
    geometry.setPositions(pointArr)
    geometry.setColors(colors)
    let line = new Line2(geometry, material)
    line.userData['type'] = 'topology'
    line.userData['subject1'] = object1.uuid
    line.userData['subject2'] = object2.uuid
    line.computeLineDistances();
    line.scale.set(1, 1, 1);
    topologyGroup.add(line);
    // signals.objectAdded.dispatch(line)
    render()
  })

  signals.deviceDeline.add(function () {
    let lines = topologyGroup.children
    while (lines.length > 0) {
      topologyGroup.remove(lines[lines.length - 1])
    }
    render()
  })


  /**
   *
   */
  signals.deviceFunction.add(param => {
    let deviceFunction = new DeviceObject(param.instanceId);

    switch (param['functionType']) {
      case 'move':
        deviceFunction.move(param.args[0], param.args[1])
        break;
      case 'scale':
        deviceFunction.zoom(param.args[0], param.args[1])
        break;
      case 'rotate':
        deviceFunction.rotate(param.args[0], param.args[1], param.args[2])
        break;
      default:
        break
    }
    deviceFunction = null
  })

  /**
   * zhouwr add by 2020-11-05
   * 增加此代码，用于点击对象时选中对象
   */
  signals.objectAdded.add(function (object) {
    //
    object.traverse(function (child) {
      if(child.userData.type === 'scene') return 0
      objects.push(child)
    })
  })

  signals.objectChanged.add(function (object) {
    if (displayer.selected === object) {
      selectionBox.setFromObject(object)
    }

    if (object.isPerspectiveCamera) {
      object.updateProjectionMatrix()
    }

    if (displayer.helpers[object.id] !== undefined) {
      displayer.helpers[object.id].update()
    }

    render()
  })

  signals.helperAdded.add(function (object) {
    var picker = object.getObjectByName('picker')

    if (picker !== undefined) {
      objects.push(picker)
    }
  })

  signals.helperRemoved.add(function (object) {
    var picker = object.getObjectByName('picker')

    if (picker !== undefined) {
      objects.splice(objects.indexOf(picker), 1)
    }
  })

  signals.materialChanged.add(function () {
    render()
  })

  // fog

  var currentFogType = null

  signals.sceneFogChanged.add(function (fogType, fogColor, fogNear, fogFar, fogDensity) {
    if (currentFogType !== fogType) {
      switch (fogType) {
        case 'None':
          scene.fog = null
          break
        case 'Fog':
          scene.fog = new THREE.Fog()
          break
        case 'FogExp2':
          scene.fog = new THREE.FogExp2()
          break
      }

      currentFogType = fogType
    }

    if (scene.fog) {
      if (scene.fog.isFog) {
        scene.fog.color.setHex(fogColor)
        scene.fog.near = fogNear
        scene.fog.far = fogFar
      } else if (scene.fog.isFogExp2) {
        scene.fog.color.setHex(fogColor)
        scene.fog.density = fogDensity
      }
    }

    render()
  })

  signals.windowResize.add(function () {
    let width = document.getElementById('center_top').offsetWidth -2
    let height = document.getElementById('center_top').offsetHeight -2
    console.log('windowResize....', width, height)

    displayer.VIEWPORT_WIDTH = width
    displayer.VIEWPORT_HEIGHT = height
    displayer.DEFAULT_CAMERA.aspect = width / height
    displayer.DEFAULT_CAMERA.updateProjectionMatrix()


    camera.aspect = width / height
    camera.updateProjectionMatrix()

    renderer.setSize(width, height)

    render()
  })

  /**
   * zhouwr add by 2020-11-11
   * @description 手动设置reader的大小
   */
  signals.setReaderSized.add(function (width, height) {
    displayer.DEFAULT_CAMERA.aspect = width / height
    displayer.DEFAULT_CAMERA.updateProjectionMatrix()

    camera.aspect = width / height
    camera.updateProjectionMatrix()

    renderer.setSize(width, height)
    renderer.setClearAlpha(0);

    render()
  })

  signals.enableTransformControlsChanged.add(enable => {
    enableTransformControls = enable
    render()
  })

  // animations

  var clock = new THREE.Clock() // only used for animations

  function animate() {
    requestAnimationFrame(animate)

    var mixer = displayer.mixer
    var delta = clock.getDelta()

    var needsUpdate = false

    if (mixer.stats.actions.inUse > 0) {
      mixer.update(delta)
      needsUpdate = true
    }

    if (viewHelper.animating === true) {
      viewHelper.update(delta)
      needsUpdate = true
    }

    if (sunlight.animating === true) {
      sunlight.update(delta)
      needsUpdate = true
    }

    if (needsUpdate === true) render()
  }

  requestAnimationFrame(animate)

  var startTime = 0
  var endTime = 0
  function render() {
    startTime = performance.now()

    renderer.setViewport(0, 0, container.dom.offsetWidth, container.dom.offsetHeight)
    renderer.render(scene, displayer.camera)

    if (camera === displayer.camera) {
      renderer.autoClear = false
      if (showSceneHelpers === true) renderer.render(sceneHelpers, camera)
      viewHelper.render(renderer)
      sunlight.render(renderer)
      renderer.autoClear = true
    }

    displayer.signals.sceneRendered.dispatch(performance.now() - startTime)
  }

  return container
}

export {Viewport}